added SSCLI 1.0
[windows-sources.git] / shared source / sscli20 / tools / nmake / rpn.cpp
blob1d493cf17d4cc4c5cf3b023346c2cda052a80cbf
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
14 // ==--==
15 // RPN.C -- expression evaluator
17 // Purpose:
18 // This module contains NMAKE's expression evaluator routines.
20 #include "precomp.h"
21 #ifdef _MSC_VER
22 #pragma hdrstop
23 #endif
25 #include "rpn.h"
27 char * GetEndQuote(void);
28 char * GetEndBracket(void);
29 void check_syntax_error(UCHAR);
30 void type_and_val(UCHAR, INT_PTR);
31 void pushIntoList(void);
32 void printList(void);
33 BOOL handleExpr(void);
34 BOOL handleExists(char*);
35 BOOL handleDefines(char*);
36 void getTok(void);
37 BOOL do_binary_op(UCHAR);
38 BOOL do_unary_op(UCHAR);
39 UCHAR match(char *tokPtr);
40 void chkInvocAndPush(RPNINFO *pListPtr);
42 #define TEMPSTACKSIZE 512 // size of temporary stack
43 #define LISTSIZE 1024 // size of list of rpn-form items
45 RPNINFO tempStack[TEMPSTACKSIZE]; // temporary/operand stack
46 RPNINFO rpnList[LISTSIZE]; // list of items in rpn order
47 char * text; // pointer to expr text in lbufPtr
48 UCHAR prevTok; // initial token put on tempstack
49 BOOL done; // true if there are no more tokens
50 UCHAR errRow; // first token is '(' so error table
51 // row val is 3. See check_syntax....
52 RPNINFO * pTop; // top item on tempStack
53 RPNINFO * pList; // next free slot in list
54 RPNINFO * pEnd = &(tempStack[TEMPSTACKSIZE-1]);
55 RPNINFO * pListEnd = &(rpnList[LISTSIZE-1]);
56 RPNINFO tokRec;
59 // do_binary_op() - do operation on two stack operands
61 // arguments: type - operator type code
63 // actions : pops first operand from the stack (tempStack).
64 // checks the types of the two operands (the operand
65 // that was popped as well as the operand currently
66 // on top of the stack).
67 // if both operands are integers then do the operation
68 // else if both operands are strings and operation is
69 // the equality operation then do it.
70 // else return FALSE ( illegal operation )
72 // modifies : tempStack - top element will now be the result of
73 // the operation.
75 BOOL
76 do_binary_op(
77 UCHAR type
80 INT_PTR *left;
81 INT_PTR *right;
82 RPNINFO *pOldTop;
84 pOldTop = pTop--; // pop one item off stack, with a ptr to it
85 right = &pOldTop->valPtr;
86 left = &pTop->valPtr;
88 if ((pOldTop->type == INTEGER) && (pTop->type == INTEGER)) {
89 switch (type) {
90 case LOGICAL_OR:
91 *left = *left || *right;
92 break;
94 case LOGICAL_AND:
95 *left = *left && *right;
96 break;
98 case BIT_OR:
99 *left |= *right;
100 break;
102 case BIT_XOR:
103 *left ^= *right;
104 break;
106 case BIT_AND:
107 *left &= *right;
108 break;
110 case NOT_EQUAL:
111 *left = *right != *left;
112 break;
114 case EQUAL:
115 *left = *right == *left;
116 break;
118 case GREATER_THAN:
119 *left = *left > *right;
120 break;
122 case LESS_THAN:
123 *left = *left < *right;
124 break;
126 case GREATER_EQ:
127 *left = *left >= *right;
128 break;
130 case LESS_EQ:
131 *left = *left <= *right;
132 break;
134 case SHFT_RIGHT:
135 *left >>= *right;
136 break;
138 case SHFT_LEFT:
139 *left <<= *right;
140 break;
142 case BINARY_MINUS:
143 *left -= *right;
144 break;
146 case ADD:
147 *left += *right;
148 break;
150 case MODULUS:
151 if (!*right)
152 makeError(line, DIVIDE_BY_ZERO);
153 *left %= *right;
154 break;
156 case DIVIDE:
157 if (!*right)
158 makeError(line, DIVIDE_BY_ZERO);
159 *left /= *right;
160 break;
162 case MULTIPLY:
163 *left *= *right;
164 break;
166 default:
167 return(FALSE);
168 break;
170 } else if ((pOldTop->type == STR) && (pTop->type == STR)) {
171 int i = _tcscmp((char *) *left, (char *) *right);
173 switch (type) {
174 case NOT_EQUAL:
175 *left = (i != 0);
176 break;
178 case EQUAL:
179 *left = (i == 0);
180 break;
182 case GREATER_THAN:
183 *left = (i > 0);
184 break;
186 case LESS_THAN:
187 *left = (i < 0);
188 break;
190 case GREATER_EQ:
191 *left = (i >= 0);
192 break;
194 case LESS_EQ:
195 *left = (i <= 0);
196 break;
198 default:
199 return(FALSE);
200 break;
203 pTop->type = INTEGER;
204 } else {
205 return(FALSE);
208 return(TRUE);
212 // do_unary_op() - do operation on top stack operand
214 // arguments: type - operator type code
216 // actions : checks the type of the top operand on the stack
217 // if operand is an integer then do the operation
218 // else return FALSE ( illegal operation )
220 // modifies : tempStack - top element will now be the result of
221 // the operation.
223 BOOL
224 do_unary_op(
225 UCHAR type
228 INT_PTR *top;
230 top = &pTop->valPtr;
232 if (pTop->type == INTEGER) {
233 switch (type) {
234 case UNARY_MINUS:
235 *top = -*top;
236 break;
238 case COMPLEMENT:
239 *top = ~*top;
240 break;
242 case LOGICAL_NOT:
243 *top = !*top;
244 break;
246 default:
247 return(FALSE);
248 break;
250 } else {
251 return(FALSE);
254 return(TRUE);
259 // GetEndQuote
261 // Return the pointer to the next double-quote character in text. A
262 // double-quote followed immediately by a double-quote is skipped.
264 // text : the global ptr to the buffer is moved up beyond this string.
266 char *
267 GetEndQuote()
269 char *pStart;
271 for (pStart = ++text; *text; ++text)
272 if (*text == '\"') {
273 if (text[1] == '\"')
274 ++text;
275 else
276 break;
279 if (!*text)
280 makeError(line, SYNTAX_MISSING_END_CHAR, '\"');
282 *text++ = '\0'; // null byte over closing quote
283 return(pStart);
287 // GetEndBracket
289 // Lexes a program invocation.
291 // Program invocation is of the form: [ prog <arglist> ].
292 // Process escaped ']' here because this is where we do the lexing.
294 // text : the global ptr to the buffer is moved up beyond this string.
296 char *
297 GetEndBracket()
299 char *pStart;
301 for (pStart = ++text; *text; text = _tcsinc (text)) {
302 if (*text == ESCH && text[1] == ']')
303 memmove(text, text + 1, 1 + _tcslen(text + 1));
304 else if (*text == ']')
305 break;
308 if (!*text)
309 makeError(line, SYNTAX_MISSING_END_CHAR, ']');
311 *text++ = '\0'; // null byte over closing bracket
312 return(pStart);
316 // check_syntax_error() - check if there is a syntax error in expr
318 // arguments: type - type of the current token
320 // actions: checks the type of the current token against the type
321 // of the previous token.
323 // ERROR_TABLE :
324 // 2nd tok
326 // alpha op unary_op ( )
327 // ------------------------------------------------
328 // alpha | 0 | 1 | 0 | 0 | 1 |
329 // -------------------------------------------------
330 // op | 1 | 0 | 1 | 1 | 0 |
331 // -------------------------------------------------
332 // unary_op | 1 | 0 | 0 | 1 | 0 |
333 // -------------------------------------------------
334 // ( | 1 | 0 | 1 | 1 | 0 |
335 // -------------------------------------------------
336 // ) | 0 | 1 | 0 | 0 | 1 |
337 // -------------------------------------------------
338 // 1st tok.
340 // alpha : a primary ( integer, str, prog. invoc. )
341 // op : a binary operator
342 // unary_op : a unary operator ( ~, !, - ). A ZERO in the slot => error
344 // NOTE: ANY CHANGES TO THE TYPE VALUES WILL AFFECT THIS ROUTINE.
346 void
347 check_syntax_error(
348 UCHAR newTok
351 // extern UCHAR errRow;
352 UCHAR errCol;
354 if (newTok == LEFT_PAREN)
355 errCol = 3;
356 else if (newTok == RIGHT_PAREN)
357 errCol = 4;
358 else if (newTok > LOGICAL_NOT)
359 errCol = 0;
360 else if (newTok > MULTIPLY)
361 errCol = 2;
362 else
363 errCol = 1;
365 if (!errTable[errRow][errCol])
366 makeError(line, SYNTAX_INVALID_EXPR);
367 errRow = errCol; // this becomes the first token the next time
371 // type_and_val()
373 // arguments: type - the type code of the present operator.
374 // val - ptr to a str/or integer
376 // initialises a record with the type code, after checking for any
377 // syntax errors. The new token is checked against the previous token
378 // for illegal combinations of tokens.
379 // initialises the record with the integer value/string ptr.
381 void
382 type_and_val(
383 UCHAR type,
384 INT_PTR val
387 // extern RPNINFO tokRec; // returned to handleExpr
388 // extern UCHAR prevTok; // token last seen
390 check_syntax_error(type);
391 prevTok = type;
392 tokRec.type = type;
393 tokRec.valPtr = val;
397 // match()
399 // arguments: tokPtr - ptr to a token string ( in tokTable )
401 // actions : looks for a substring in the expression buffer
402 // pointed to by 'text', that matches the given token.
403 // if substring found, returns TRUE, else returns FALSE.
405 UCHAR
406 match(
407 char *tokPtr
410 // extern char *text;
411 char *t = text;
413 while (*tokPtr && (*t == *tokPtr)) {
414 t++;
415 tokPtr++;
417 if (!*tokPtr) {
418 text = t;
419 return(TRUE);
421 return(FALSE);
425 // getTok()
427 // arguments: none
429 // gets a token from the expression buffer.
430 // if the current char from the buffer is a space/tab, skip space/tabs
431 // until we get a non-space char ( could be NULL char ).
432 // Check if we are now at the beginning of one of the tokens in the
433 // tokenTable. This covers most tokens.
434 // Check if we have a minus. If a minus and the previous token was an
435 // integer, this is a binary minus, else a unary minus.
436 // If the current char is a double-quote, we are at the start of a
437 // string-token.
438 // If the current char is a '[', we are at the start of a program
439 // invocation. In both cases, the escape character is '\\'.
440 // If current char is a digit, we have a constant ( integer ).
441 // Else we have defined(ID).
442 // If none of the above, if current char is NULL, break out, else
443 // report error ( illegal character string has been found ).
445 // If we came to the NULL char at the end of the buffer, set global
446 // flag 'done' to TRUE, return a RIGHT_PAREN to match the opening
447 // LEFT_PAREN.
450 // modifies: text : ptr to expression buffer.
451 // prevTok: thru' calls to type_and_val().
452 // done : at end of buffer
453 // errRow : index into error table, thru calls to
454 // type_and_val()
455 // returns : token in tokRec(global, static to the module). The
456 // token has the new type/integer/ptr values.
458 void
459 getTok()
461 // extern UCHAR prevTok;
462 // extern BOOL done;
463 char c;
464 TOKTABREC *p;
465 char *ptr;
466 long constant;
468 c = *text;
469 if (c == ' ' || c == '\t') {
470 while(_istspace(c))
471 c = *++text; // skip white spaces
474 if (IS_OPERATORCHAR(c)) {
475 for (p = tokTable; p->op_str && !match(p->op_str); p++)
477 } else {
478 // make p point to last entry in table
479 p = &tokTable[(sizeof(tokTable) / sizeof(TOKTABREC)) - 1];
482 if (p->op_str) {
483 type_and_val(p->op, 0);
484 } else
485 if (c == '-') { // now check if binary or unary minus to be returned
486 text++;
487 if (prevTok == INTEGER)
488 type_and_val(BINARY_MINUS, 0);
489 else
490 type_and_val(UNARY_MINUS, 0);
491 } else
492 if (c == '\"') {
493 type_and_val(STR, (INT_PTR) GetEndQuote());
494 } else
495 if (c == '[') {
496 type_and_val(PROG_INVOC_STR, (INT_PTR) GetEndBracket());
497 } else { // integers and IDs handled here
498 if (_istdigit(c)) {
499 char *pNumber = text;
501 errno = 0; // Accept decimal, octal or hex no
502 constant = strtol(text, &text, 0);
503 if (errno == ERANGE) {
504 *text = '\0';
505 makeError(line, CONST_TOO_BIG, pNumber);
508 if (_totupper(*text) == 'L')
509 text++;
510 type_and_val(INTEGER, constant);
511 } else { // defined(ID) comes here
512 if (c) {
513 if (!_tcsnicmp(text, "DEFINED", 7)) {
514 if (!(ptr = _tcschr(text, '(')))
515 makeError(line, SYNTAX_INVALID_EXPR);
516 ptr++;
517 text = ptr + _tcscspn(ptr, ")");
518 *text++ = '\0';
519 type_and_val(INTEGER, handleDefines(ptr));
521 else if (!_tcsnicmp(text, "EXIST", 5)) {
522 if (!(ptr = _tcschr(text, '(')))
523 makeError(line, SYNTAX_INVALID_EXPR);
524 ptr++;
525 text = ptr + _tcscspn(ptr, ")");
526 *text++ = '\0';
527 type_and_val(INTEGER, handleExists(ptr));
529 else
530 makeError(line, SYNTAX_INVALID_EXPR);
531 } else { // we are now at the end of the string (c is null)
532 done = TRUE;
533 type_and_val(RIGHT_PAREN, 0); // this is the last token
540 // chkInvocAndPush() - check if program invocation required
542 // arguments: pListPtr - might have a program invocation string
543 // present.
545 // actions : if this is a program invocation string, make the
546 // program invocation.
547 // the return value is got and placed on the stack.
548 // the type of the new stack element is now INTEGER.
549 // else place list item on stack.
551 // in either case it moves one item from list to stack.
553 void
554 chkInvocAndPush(
555 RPNINFO *pListPtr
558 ++pTop;
559 if (pListPtr->type == PROG_INVOC_STR) {
560 pTop->valPtr = execLine((char *) pListPtr->valPtr, FALSE, TRUE, FALSE, NULL);
561 pTop->type = INTEGER;
562 } else {
563 *pTop = *pListPtr;
568 // processList()
570 // arguments: none
572 // actions : remove an item from the list.
573 // if the item is an operand, place it on the operand
574 // stack (tempStack).
575 // if the operand is a program invocation string, make
576 // the invocation, place the return code on stack.
577 // if the item is an operator, call the function to
578 // do the operation on one/two elements on tempStack.
580 // finally, check if there is exactly one item on stack.
581 // if this item has a value of zero, return FALSE.
582 // else return TRUE.
583 // if more than one item on stack, abort with error.
585 // modifies: pTop - ptr to top of tempStack.
586 // pList - ptr to next position in list.
588 BOOL
589 processList()
591 // extern RPNINFO *pList;
592 // extern RPNINFO *pTop;
593 RPNINFO *pTemp;
594 BOOL (* func)(UCHAR);
596 for (pTemp = rpnList; pTemp < pList; pTemp++) {
597 if (pTemp->type > LOGICAL_NOT) { // operand
598 chkInvocAndPush(pTemp);
599 } else {
600 if (pTemp->type > MULTIPLY)
601 func = do_unary_op;
602 else
603 func = do_binary_op;
605 if (!(*func)(pTemp->type))
606 makeError(line, BAD_OP_TYPES);
610 if ((pTop == tempStack) && (pTop->type == INTEGER))
611 if (!pTop->valPtr)
612 return(FALSE);
613 else
614 return(TRUE);
615 else
616 makeError(line, SYNTAX_INVALID_EXPR);
618 return(FALSE);
622 // pushIntoList()
624 // arguments: none
626 // actions : pops an item from the tempStack and pushes it onto
627 // the list. checks list for overflow ( internal error )
628 // and tempStack for underflow ( syntax error in expr ).
630 // modifies: tempTop - index of top of tempStack.
631 // nextInList - index to next position in list.
633 void
634 pushIntoList()
636 if (pTop < tempStack)
637 makeError(line, SYNTAX_INVALID_EXPR);
639 if (pList > pListEnd)
640 makeError(line, EXPR_TOO_LONG_INTERNAL);
642 #if !defined(NDEBUG)
643 // Keep track of the high water mark on the stack just for grins
645 static int iStackMax = 0;
646 if ( pList - rpnList > iStackMax )
647 iStackMax = (int) (pList - rpnList);
649 #endif
651 *pList++ = *pTop--;
655 // handleExpr()
657 // arguments: text - pointer to the buffer that has the expression.
659 // actions : calls getTok() to get tokens from the buffer. Places
660 // tokens in a tempStack, and moves them into a list in
661 // reverse-polish order.
663 // We need the list so that ALL syntax errors are caught
664 // BEFORE processing of the expression begins (especially
665 // program invocations that have side effects)
667 // Once the list is available, an operand stack is used
668 // Items are popped and pushed from this stack by the
669 // evaluation routines (add, mult, negate etc.)
671 // we don't really need a separate operand stack. the
672 // tempStack has served its purpose when the list is
673 // formed and so it may be used for operand processing.
675 BOOL
676 handleExpr()
678 // extern RPNINFO tokRec;
679 BOOL fRParen; // was the token got a right paren?
680 // extern BOOL done;
681 // extern RPNINFO *pTop, *pList;
682 // extern UCHAR errRow;
683 // extern UCHAR prevTok;
685 pTop = tempStack;
686 pList = rpnList;
687 done = FALSE;
688 errRow = 3; // row for the first token put in,left paren
689 prevTok = LEFT_PAREN;
690 type_and_val(LEFT_PAREN, 0);
691 *pTop = tokRec;
693 while (!done) { // while there are more tokens in buffer
694 getTok();
695 fRParen = FALSE;
696 if (tokRec.type != LEFT_PAREN) {
697 while (precVector[tokRec.type] <= precVector[pTop->type]) {
698 if (!precVector[tokRec.type]) { // if RIGHT_PAREN pop till a
699 // left paren is seen
700 while (pTop->type != LEFT_PAREN)
701 pushIntoList();
702 fRParen = TRUE;
703 if (pTop < tempStack) {
704 makeError(line, SYNTAX_INVALID_EXPR);
705 } else {
706 pTop--; // pop the left paren
707 break;
709 } else {
710 pushIntoList();
714 // if token is a left paren, it has to go on the stack
715 if (!fRParen) {
716 if (pTop == pEnd)
717 makeError(line, EXPR_TOO_LONG_INTERNAL);
718 else
719 *++pTop = tokRec;
723 // check the stack here for not empty state
724 if (pTop != tempStack - 1)
725 makeError(line, SYNTAX_INVALID_EXPR);
726 return(processList());
730 // handleDefines()
732 // arguments: t pointer to buffer that has the identifier
734 // actions: Checks if one of 'ID' is present.
735 // Aborts with error if more IDs present.
736 // Is called for ifdef/ifndef/defined(ID).
738 // returns : TRUE if ID found in table. FALSE otherwise.
740 BOOL
741 handleDefines(
742 char *t
745 char *s;
747 s = _tcstok(t, " \t");
748 if (_tcstok(NULL, " \t")) {
749 makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
752 if (!s) {
753 makeError(line, MISSING_ARG_BEFORE_PAREN);
756 if (findMacro(s)) {
757 return(TRUE);
760 return(FALSE);
764 // handleExists()
766 // arguments: t pointer to buffer that has the identifier
768 // actions: Checks if 'name' is a valid file/directory
769 // Aborts with error if more names present.
770 // Is called for exist(name).
772 // returns : TRUE if ID found in table. FALSE otherwise.
774 BOOL
775 handleExists(
776 char *_t
779 char *s;
780 char *szUnQuoted = NULL;
781 BOOL fResult = FALSE;
782 char *szDelim;
783 char *t;
785 // make local copy, strip blank space before and after string
786 char *tSav = t = makeString(_t);
787 while (*t && WHITESPACE (*t)) {
788 t++;
790 s = t + _tcslen(t);
791 while (s > t) {
792 s = _tcsdec(t, s);
793 if (WHITESPACE (*s)) {
794 *s = '\0';
796 else {
797 break;
801 szDelim = ('\"' == *t) ? (char *)"\t" : (char *)" \t";
802 // if id starts with a quote,
803 // use "\t" instead of " \t" in _tcstok
804 // (handle paths with embedded spaces)
805 s = _tcstok(t, szDelim);
806 if (_tcstok(NULL, szDelim)) {
807 makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
810 if (NULL == s || NULL == (szUnQuoted = unQuote(s))) { // handle quoted names
811 makeError(line, MISSING_ARG_BEFORE_PAREN);
814 if (!_access(szUnQuoted, 0x00)) { // existence check
815 fResult = TRUE;
818 FREE(szUnQuoted);
819 FREE(tSav);
821 return(fResult);
825 // evalExpr()
827 // arguments: t pointer to buffer that has the expression
828 // kind specifies if it is if/ifdef/ifndef etc.
830 // returns : TRUE if expression evaluates to true.
831 // FALSE otherwise.
833 BOOL
834 evalExpr(
835 char *t,
836 UCHAR kind
839 if (!*t) {
840 makeError(line, SYNTAX_MISSING_DIRECTIVE);
843 switch (kind) {
844 case IFDEF_TYPE:
845 case ELSE_IFDEF_TYPE:
846 return(handleDefines(t));
848 case IFNDEF_TYPE:
849 case ELSE_IFNDEF_TYPE:
850 return((BOOL)!handleDefines(t));
852 default:
853 text = t;
854 return(handleExpr());